/*
 ============================================================================
 Name        : tp6
 Author      : Pablo Henrique dos Reis
 Version     :
 Copyright   : 
 Description : Trabalho prático 6
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>

#include "cache.h"
#include "entradaSaida.h"

#define LEITURA "r"
#define ESCRITA "w"

#define MAX_LINHA 30
#define FIFO 0
#define LRU 1
#define CUSTOMIZADO 2

/**
 * Extrai numeros separados por espaço de uma string
 */
int *extrairNumeros(char *str, int qtd) {

	int *retorno  = (int*)malloc(sizeof(int)*qtd);
	int indice = 0;
	char * pch = (char*) strtok(str, " ");

	while (pch != NULL) {
		int inteiro = atoi(pch);
		retorno[indice] = inteiro;

		indice++;
		pch = (char *) strtok(NULL, " ");
	}

	return retorno;
}

/**
 * Preenche a lista de chunks com dados do arquivo
 */
void preencherChunks(ListaChunk *l, char *nomeArquivo) {

	FILE *arq = NULL;
	abrirArquivo(&arq, nomeArquivo, LEITURA);
	char buffer[MAX_LINHA];

	while(lerLinha(buffer, MAX_LINHA, &arq) != NULL) {
		int *dados = extrairNumeros(buffer,3);
		int idVideo = dados[0];
		int indiceChunk = dados[1];
		int tamanhoChunk = dados[2];
		free(dados);
		inserirChunk(l,idVideo, indiceChunk, tamanhoChunk, -1);
	}
	fecharArquivo(&arq);
}

/**
 * Inserir no cache um chunk de video
 * C -> ponteiro para o Cache
 * chunk -> Chunk de video
 * profundidade -> indica o vizinho do chunk que será  inserido no cache, 0 insere o chunk do parametro, 1 o próximo e -1 o anterior
 * politica -> politica para remover algum elementeo do cache caso não haja espaço
 */
void inserirNoCache(Cache *C, PChunk chunk, int profundidade, int politica) {

	PItemCache itemCache = getNoCache(C,chunk);
	if(itemCache != NULL) return;

	int i;
	PChunk aux = chunk;

	if(profundidade < 0) {
		profundidade = profundidade * (-1);
		for(i=0; i < profundidade; i++) {
			if(aux == NULL) return;
			aux = aux->ant;
		}

	} else {
		for(i=0; i < profundidade; i++) {
			if(aux == NULL) return;
			aux = aux->prox;
		}
	}

	while(aux != NULL && !inserirItemCache(C,aux) && !isCacheVazio(*C)) {
		removerItem(C,politica);
	}
}

/**
 * Função principal
 */
int main(int argc, char *argv[]) {

	//lendo os parâmetro nome do arquivo
	char *nomeArquivo = argv[1];
	//lendo o parâmetro politica de substituicao do cache
	int politica = atoi(argv[2]);
	//lendo o parâmetro tamanho da cache
	int tamanhoCache = atoi(argv[3]);

	ListaChunk L;
	Cache C;

	double totalAcessos = 0;
	double totalAcertos = 0;
	double somaOcupacao = 0;


	criarListaChunk(&L);
	preencherChunks(&L, nomeArquivo);
	criarCache(&C,tamanhoCache);

	PChunk aux = L.Primeiro->prox;
	while (aux != NULL)
	{
		totalAcessos++;
		//printf("%d %d %d ", aux->itemChunk.idVideo,aux->itemChunk.indiceChunk, aux->itemChunk.tamanhoChunk);
		PItemCache itemCache = getNoCache(&C,aux);
		if(itemCache != NULL) {
			//printf("1");
			totalAcertos++;
		} else {
			//printf("0");

			if(politica == CUSTOMIZADO) {

				int numVizinhos = C.size / aux->itemChunk.tamanhoChunk / 3 ;
				int i;
				for(i = (numVizinhos / 2); i > 0 ; i--) {
					inserirNoCache(&C,aux,i,politica);
					inserirNoCache(&C,aux,i*(-1),politica);
				}

			}
			inserirNoCache(&C,aux,0,politica);
		}
		somaOcupacao += C.ocup;
		if(C.ocup < 0) {
			printf("erro");
		}
		//printf(" %d", C.ocup);
		aux = aux->prox;
		//printf("\n");
	}

	//calculos percentuais
	double porcentagemAcertos = ( totalAcertos / totalAcessos) * 100;
	double mediaOcupacao =  somaOcupacao / totalAcessos;
	double porcentagemMediaOcupacao = mediaOcupacao/ tamanhoCache * 100;
	double ganhoCache = (porcentagemAcertos + porcentagemMediaOcupacao) / 2;

	printf("%3.2lf %3.2lf %3.2lf", porcentagemAcertos, porcentagemMediaOcupacao, ganhoCache);

	liberaCache(C);
	liberaListaChunk(L);

	return 0;
}
